1 //+-----------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // Implements the BindStatusCallback class of PresentationHost.
12 // Ported ByteRangeDownloader to WCP
14 // Ported Windows->DevDiv. See SourcesHistory.txt.
16 //------------------------------------------------------------------------
18 #include "Precompiled.hxx"
19 #include "BindStatusCallback.hxx"
20 #include "HostShim.hxx"
21 #include "ShimUtilities.hxx"
24 static WCHAR
* rgBS_Notifications
[] =
27 L
"BINDSTATUS_FINDINGRESOURCE",
28 L
"BINDSTATUS_CONNECTING",
29 L
"BINDSTATUS_REDIRECTING",
30 L
"BINDSTATUS_BEGINDOWNLOADDATA",
31 L
"BINDSTATUS_DOWNLOADINGDATA",
32 L
"BINDSTATUS_ENDDOWNLOADDATA",
33 L
"BINDSTATUS_BEGINDOWNLOADCOMPONENTS",
34 L
"BINDSTATUS_INSTALLINGCOMPONENTS",
35 L
"BINDSTATUS_ENDDOWNLOADCOMPONENTS",
36 L
"BINDSTATUS_USINGCACHEDCOPY",
37 L
"BINDSTATUS_SENDINGREQUEST",
38 L
"BINDSTATUS_CLASSIDAVAILABLE",
39 L
"BINDSTATUS_MIMETYPEAVAILABLE",
40 L
"BINDSTATUS_CACHEFILENAMEAVAILABLE",
41 L
"BINDSTATUS_BEGINSYNCOPERATION",
42 L
"BINDSTATUS_ENDSYNCOPERATION",
43 L
"BINDSTATUS_BEGINUPLOADDATA",
44 L
"BINDSTATUS_UPLOADINGDATA",
45 L
"BINDSTATUS_ENDUPLOADDATA",
46 L
"BINDSTATUS_PROTOCOLCLASSID",
47 L
"BINDSTATUS_ENCODING",
48 L
"BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE",
49 L
"BINDSTATUS_CLASSINSTALLLOCATION",
50 L
"BINDSTATUS_DECODING",
51 L
"BINDSTATUS_LOADINGMIMEHANDLER",
52 L
"BINDSTATUS_CONTENTDISPOSITIONATTACH",
53 L
"BINDSTATUS_FILTERREPORTMIMETYPE",
54 L
"BINDSTATUS_CLSIDCANINSTANTIATE",
55 L
"BINDSTATUS_IUNKNOWNAVAILABLE",
56 L
"BINDSTATUS_DIRECTBIND",
57 L
"BINDSTATUS_RAWMIMETYPE",
58 L
"BINDSTATUS_PROXYDETECTING",
59 L
"BINDSTATUS_ACCEPTRANGES",
60 L
"BINDSTATUS_COOKIE_SENT",
61 L
"BINDSTATUS_COMPACT_POLICY_RECEIVED",
62 L
"BINDSTATUS_COOKIE_SUPPRESSED",
63 L
"BINDSTATUS_COOKIE_STATE_UNKNOWN",
64 L
"BINDSTATUS_COOKIE_STATE_ACCEPT",
65 L
"BINDSTATUS_COOKIE_STATE_REJECT",
66 L
"BINDSTATUS_COOKIE_STATE_PROMPT",
67 L
"BINDSTATUS_COOKIE_STATE_LEASH",
68 L
"BINDSTATUS_COOKIE_STATE_DOWNGRADE",
69 L
"BINDSTATUS_POLICY_HREF",
70 L
"BINDSTATUS_P3P_HEADER",
71 L
"BINDSTATUS_SESSION_COOKIE_RECEIVED",
72 L
"BINDSTATUS_PERSISTENT_COOKIE_RECEIVED",
73 L
"BINDSTATUS_SESSION_COOKIES_ALLOWED",
78 //******************************************************************************
80 // CBindStatusCallback::CBindStatusCallback
82 //******************************************************************************
84 CBindStatusCallback::CBindStatusCallback()
86 DPF(8, _T("CBindStatusCallback::CBindStatusCallback(%08X)"), this);
93 m_fTerminatedFLB
= FALSE
;
94 m_Mime
= MimeType_Unknown
;
96 m_hrBindResult
= S_FALSE
;
99 m_hMimeArrivedEvent
= ::CreateEvent(
100 NULL
, // Default security, not inherited.
101 TRUE
, // Manual reset event.
102 FALSE
, // Initially unsignaled.
106 m_hManifestAvailableEvent
= ::CreateEvent(
107 NULL
, // Default security, not inherited.
108 TRUE
, // Manual reset event.
109 FALSE
, // Initially unsignaled.
113 ::ResetEvent(CHostShim::GetInstance()->GetDownloadCompletedEvent());
115 m_pDownloadInfo
= NULL
;
118 CBindStatusCallback::~CBindStatusCallback()
120 DPF(8, _T("CBindStatusCallback::~CBindStatusCallback(%08X)"), this);
122 if (m_hMimeArrivedEvent
!= NULL
)
124 ::CloseHandle(m_hMimeArrivedEvent
);
125 m_hMimeArrivedEvent
= NULL
;
128 if (m_hManifestAvailableEvent
!= NULL
)
130 ::CloseHandle(m_hManifestAvailableEvent
);
131 m_hManifestAvailableEvent
= NULL
;
134 ReleaseInterface(m_pStream
);
135 ReleaseInterface(m_pflb
);
137 //DASSERT(m_pib == NULL);
138 ReleaseInterface(m_pib
);
141 //******************************************************************************
143 // CBindStatusCallback::QueryInterface(REFIID riid,
146 //******************************************************************************
148 STDMETHODIMP
CBindStatusCallback::QueryInterface(REFIID riid
,
149 __out LPVOID
*ppReturn
)
151 HRESULT hr
= E_NOINTERFACE
;
153 DPF(8, _T("CBindStatusCallback::QueryInterface(%08X)"), this);
157 if (riid
== IID_IUnknown
)
161 else if (riid
== IID_IBindStatusCallback
)
168 ((LPUNKNOWN
)*ppReturn
)->AddRef();
175 //******************************************************************************
177 // CBindStatusCallback::AddRef()
179 //******************************************************************************
181 STDMETHODIMP_(ULONG
) CBindStatusCallback::AddRef()
184 DPF(10, _T("CBindStatusCallback::AddRef(%08X)- %d"), this, m_nObjRefCount
);
185 return m_nObjRefCount
;
188 //******************************************************************************
190 // CBindStatusCallback::Release()
192 //******************************************************************************
194 STDMETHODIMP_(ULONG
) CBindStatusCallback::Release()
198 DPF(10, _T("CBindStatusCallback::Release(%08X)- %d"), this, m_nObjRefCount
);
200 if (m_nObjRefCount
== 0)
206 return m_nObjRefCount
;
209 //******************************************************************************
211 // CBindStatusCallback::OnStartBinding(DWORD dwReserved, IBinding* pib)
213 //******************************************************************************
215 HRESULT
CBindStatusCallback::OnStartBinding(DWORD
/* dwReserved */,
218 DPF(8, _T("CBindStatusCallback::OnStartBinding(%08X)"), this);
231 //******************************************************************************
233 // CBindStatusCallback::GetPriority(LONG *pnPriority)
235 //******************************************************************************
237 HRESULT
CBindStatusCallback::GetPriority(__out_opt LONG
*)
239 DPF(8, _T("CBindStatusCallback::GetPriority(%08X)"), this);
241 // Applications that implement the IBindStatusCallback interface can return E_UNIMPL or
242 // S_OK if they are not interested in receiving this notification.
247 //******************************************************************************
249 // CBindStatusCallback::OnLowResource(DWORD dwReserved)
251 //******************************************************************************
253 HRESULT
CBindStatusCallback::OnLowResource(DWORD
) // dwReserved)
255 DPF(8, _T("CBindStatusCallback::OnLowResource(%08X)"), this);
259 //******************************************************************************
261 // CBindStatusCallback::OnProgress(ULONG ulProgress,
262 // ULONG ulProgressMax,
263 // ULONG ulStatusCode,
264 // LPCWSTR szStatusText)
266 //******************************************************************************
268 HRESULT
CBindStatusCallback::OnProgress(ULONG
, // ulProgress,
269 ULONG ulProgressMax
, //ulProgressMax
270 ULONG ulCode
, // ulStatusCode,
271 LPCWSTR pszText
) // szStatusText)
273 EventWriteWpfHostUm_BindProgress(ulCode
, pszText
);
279 case BINDSTATUS_CACHEFILENAMEAVAILABLE
:
280 DPF(4, _T("CBindStatusCallback::OnProgress(%08X) - BINDSTATUS_CACHEFILENAMEAVAILABLE (%s) "), this, pszText
);
281 m_cacheFilename
.SetValue(pszText
);
282 ResolveMimeTypeSecondChance(pszText
);
285 case BINDSTATUS_MIMETYPEAVAILABLE
:
286 DPF(4, _T("CBindStatusCallback::OnProgress(%08X) - BINDSTATUS_MIMETYPEAVAILABLE"), this);
287 SetMimeTypeString(pszText
);
288 m_Mime
= GetMimeTypeFromString(pszText
);
289 // DD Dev10 613556 - If a misconfigured web server returns an unknown MIME type,
290 // we'll get a second chance to try to map it back to a known MIME type based on
291 // the file extension, through ResolveMimeTypeSecondChance. This will set the
292 // event regardless of the outcome.
293 if (m_Mime
!= MimeType_Unknown
)
295 ::SetEvent(m_hMimeArrivedEvent
);
299 case BINDSTATUS_BEGINDOWNLOADDATA
:
300 DPF(4, _T("CBindStatusCallback::OnProgress(%08X) - BINDSTATUS_BEGINDOWNLOADDATA(%d)"), this, ulProgressMax
);
301 m_nBytesTotal
= ulProgressMax
;
304 case BINDSTATUS_ENDDOWNLOADDATA
:
305 DPF(4, _T("CBindStatusCallback::OnProgress(%08X) - BINDSTATUS_ENDDOWNLOADDATA(%d)"), this, ulProgressMax
);
310 DPF(4, _T("CBindStatusCallback::OnProgress(%08X) - %s [%d]"), this, rgBS_Notifications
[ulCode
], ulCode
);
318 //******************************************************************************
320 // CBindStatusCallback::OnStopBinding(HRESULT hresult, LPCWSTR szError)
322 //******************************************************************************
324 HRESULT
CBindStatusCallback::OnStopBinding(HRESULT hr
, LPCWSTR szError
)
326 DPF(8, _T("CBindStatusCallback::OnStopBinding(%08X) - %08X"), this, hr
);
327 m_hrBindResult
= hr
; // Useful for Watson dump investigations.
328 EventWriteWpfHostUm_OnStopBinding(hr
);
332 this->BindTerminated(hr
);
341 // Either on error or on success signal the events so that PersistMoniker can continue.
342 ::SetEvent(this->m_hMimeArrivedEvent
);
343 ::SetEvent(this->m_hManifestAvailableEvent
);
344 SignalAvalonStartupRelatedEvent(CHostShim::GetInstance()->GetDownloadCompletedEvent());
349 //******************************************************************************
351 // CBindStatusCallback::GetBindInfo(DWORD* grfBINDF, BINDINFO* pbindinfo)
353 // BINDF_ASYNCHRONOUS - Value that indicates that the moniker should return immediately
354 // from IMoniker::BindToStorage or IMoniker::BindToObject. The actual
355 // result of the bind to an object or the bind to storage arrives
356 // asynchronously. The client is notified through calls to its
357 // IBindStatusCallback::OnDataAvailable or
358 // IBindStatusCallback::OnObjectAvailable method. If the client does
359 // not specify this flag, the bind operation will be synchronous, and
360 // the client will not receive any data from the bind operation until
361 // the IMoniker::BindToStorage or IMoniker::BindToObject call returns.
363 // BINDF_ASYNCSTORAGE - Value that indicates the client application calling the
364 // IMoniker::BindToStorage method prefers that the storage and stream
365 // objects returned in IBindStatusCallback::OnDataAvailable return
366 // E_PENDING when they reference data not yet available through their
367 // read methods, rather than blocking until the data becomes available.
368 // This flag applies only to BINDF_ASYNCHRONOUS operations. Note that
369 // asynchronous stream objects return E_PENDING while data is still
370 // downloading and return S_FALSE for the end of the file.
372 // BINDF_PULLDATA - Value that indicates the asynchronous moniker allows the client of
373 // IMoniker::BindToStorage to drive the bind operation by pulling the data,
374 // rather than having the moniker drive the operation by pushing the data
375 // to the client. When this flag is specified, new data is only read/downloaded
376 // after the client finishes downloading all data that is currently available.
377 // This means data is only downloaded for the client after the client does an
378 // IStream::Read operation that blocks or returns E_PENDING. When the client
379 // specifies this flag, it must be sure to read all the data it can, even data
380 // that is not necessarily available yet. When this flag is not specified, the
381 // moniker continues downloading data and calls the client with
382 // IBindStatusCallback::OnDataAvailable whenever new data is available. This
383 // flag applies only to BINDF_ASYNCHRONOUS bind operations.
385 //******************************************************************************
387 HRESULT
CBindStatusCallback::GetBindInfo(__out DWORD
* grfBINDF
,
388 __out BINDINFO
* pBindInfo
)
390 DPF(8, _T("CBindStatusCallback::GetBindInfo(%08X)"), this);
396 *grfBINDF
= BINDF_ASYNCHRONOUS
403 pBindInfo
->cbSize
= sizeof(BINDINFO
);
404 pBindInfo
->szExtraInfo
= NULL
;
405 pBindInfo
->grfBindInfoF
= 0;
406 //!!! Make sure to set BINDINFO_OPTIONS_ENABLE_UTF8 in all implementations of IBindStatusCallback.
407 //!!! It is needed for correct encoding of non-ASCII characters in URL paths, per RFC 3986 & 3987.
408 pBindInfo
->dwOptions
= BINDINFO_OPTIONS_ENABLE_UTF8
;
409 pBindInfo
->dwBindVerb
= BINDVERB_GET
;
410 pBindInfo
->szCustomVerb
= NULL
;
412 ::SecureZeroMemory(&pBindInfo
->stgmedData
, sizeof(STGMEDIUM
));
418 //******************************************************************************
420 // CBindStatusCallback::OnDataAvailable(DWORD grfBSCF,
422 // FORMATETC* pFormatetc,
423 // STGMEDIUM* pStgmed)
425 //******************************************************************************
427 HRESULT
CBindStatusCallback::OnDataAvailable(DWORD grfBSCF
,
429 __in FORMATETC
* pFormatetc
,
430 __in STGMEDIUM
* pStgmed
)
432 DPF(8, _T("CBindStatusCallback::OnDataAvailable(%08X) - (%d)"), this, dwSize
);
438 // if this is not a container, jump out.
444 //DASSERT(dwSize <= m_nBytesTotal);
446 // on first notification, cache stream handle.
447 if (grfBSCF
& BSCF_FIRSTDATANOTIFICATION
)
449 // make sure the type is an IStream
450 if ( (pStgmed
->tymed
== TYMED_ISTREAM
) &&
451 (pStgmed
->pstm
!= NULL
) )
453 //DASSERT(m_pStream == NULL);
454 m_pStream
= pStgmed
->pstm
;
459 // only read if we have a valid cached stream and
460 // we have not already read to the end.
461 if ( (m_pStream
!= NULL
) && (m_nBytesTotal
> m_nBytesRead
) )
465 BYTE pbArray
[IO_BUFFER_SIZE
];
467 // sit in loop reading stream until we are either at the end or
468 // we need to wait for more data
473 hr
= m_pStream
->Read((void *)pbArray
, sizeof(pbArray
), &cbRead
);
475 if (SUCCEEDED(hr
) || (hr
== E_PENDING
) )
477 // if we read something, track the movement
480 m_nBytesRead
+= cbRead
;
481 m_pflb
->FillAppend((void *)pbArray
, cbRead
, &cbWritten
);
483 //DASSERT(cbRead == cbWritten);
486 // handle termination
487 if (m_nBytesTotal
== m_nBytesRead
)
489 this->BindTerminated();
492 // we have reached the end, usually denoted by S_FALSE
495 DPF(4, _T("CBindStatusCallback::OnDataAvailable(%08X) - EOS (%d, %d)"), this, m_nBytesRead
, dwSize
);
500 // if E_PENDING is returned, get out.
501 // we will get another call with more data.
504 DPF(4, _T("CBindStatusCallback::OnDataAvailable(%08X) - Pos (%d, %d)"), this, m_nBytesRead
, dwSize
);
509 // if we failed, do not attempt another read. Return the failed hr
517 // on last notification, release stream
518 if (grfBSCF
& BSCF_LASTDATANOTIFICATION
)
520 ReleaseInterface(m_pStream
);
528 //******************************************************************************
530 // CBindStatusCallback::OnObjectAvailable(REFIID riid, IUnknown* punk)
532 //******************************************************************************
534 HRESULT
CBindStatusCallback::OnObjectAvailable(REFIID riid
, __in IUnknown
* punk
)
536 DPF(8, _T("CBindStatusCallback::OnObjectAvailable(%08X)"), this);
546 //******************************************************************************
548 // CBindStatusCallback::BindTerminated()
550 //******************************************************************************
552 void CBindStatusCallback::BindTerminated()
554 this->BindTerminated(E_FAIL
);
557 //******************************************************************************
559 // CBindStatusCallback::BindTerminated(HRESULT hr)
561 //******************************************************************************
563 void CBindStatusCallback::BindTerminated(HRESULT hr
)
565 if (!m_fTerminatedFLB
)
567 m_fTerminatedFLB
= TRUE
;
568 m_pflb
->Terminate(SUCCEEDED(hr
) ? TRUE
: FALSE
);
570 // Signal the download completed event.
571 SignalAvalonStartupRelatedEvent(CHostShim::GetInstance()->GetDownloadCompletedEvent());
574 //******************************************************************************
576 // CBindStatusCallback::ResolveMimeTypeSecondChance()
578 //******************************************************************************
580 void CBindStatusCallback::ResolveMimeTypeSecondChance(LPCWSTR pszLocalFile
)
582 // DD Dev10 613556 - Some misconfigured web servers return an unknown MIME type during
583 // the BINDSTATUS_MIMETYPEAVAILABLE binding phase. This second chance logic uses UrlMon
584 // functionality to map the file name of the local download location onto a CLSID which
585 // we can use to map back to the MIME type. In practice, UrlMon uses the file extension
586 // to perform this mapping in the absence of additional information.
588 if (m_Mime
== MimeType_Unknown
)
592 HRESULT hr
= GetClassFileOrMime(0, pszLocalFile
, 0, 0, 0, 0, &clsId
);
595 if (IsEqualCLSID(clsId
, CLSID_DocObjXapp
))
597 m_Mime
= MimeType_Application
;
599 else if (IsEqualCLSID(clsId
, CLSID_DocObjXaml
))
601 m_Mime
= MimeType_Markup
;
605 ::SetEvent(m_hMimeArrivedEvent
);